﻿' ******************************************************************************
' ** 
' **  Yahoo! Managed
' **  Written by Marius Häusler 2011
' **  It would be pleasant, if you contact me when you are using this code.
' **  Contact: YahooFinanceManaged@gmail.com
' **  Project Home: http://code.google.com/p/yahoo-finance-managed/
' **  
' ******************************************************************************
' **  
' **  Copyright 2011 Marius Häusler
' **  
' **  Licensed under the Apache License, Version 2.0 (the "License");
' **  you may not use this file except in compliance with the License.
' **  You may obtain a copy of the License at
' **  
' **    http://www.apache.org/licenses/LICENSE-2.0
' **  
' **  Unless required by applicable law or agreed to in writing, software
' **  distributed under the License is distributed on an "AS IS" BASIS,
' **  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
' **  See the License for the specific language governing permissions and
' **  limitations under the License.
' ** 
' ******************************************************************************



Namespace YahooManaged.Geo.GeoPlanet.API



    Public Class PlacesDownload
        Inherits Base.Download

        Public Event AsyncDownloadCompleted(ByVal sender As Base.Download, ByVal e As PlacesDownloadCompletedEventArgs)

        Private mOptions As New PlacesDownloadOptions
        Public Property Options() As PlacesDownloadOptions
            Get
                Return mOptions
            End Get
            Set(ByVal value As PlacesDownloadOptions)
                mOptions = value
            End Set
        End Property

        Public Function Download(ByVal keyword As String) As PlacesResponse
            Return Me.Download(keyword, PlaceType.Undefined)
        End Function
        Public Function Download(ByVal keyword As String, ByVal type As PlaceType) As PlacesResponse
            Return Me.DownloadPlaces(Me.PlacesUrl("places", Me.KeywordQuery(keyword, type), mOptions))
        End Function
        Public Function Download(ByVal woeids As IEnumerable(Of Long)) As PlacesResponse
            Return Me.DownloadPlaces(Me.PlacesUrl("places", Me.WoeidQuery(woeids), mOptions))
        End Function
        Public Function Download(ByVal coordinates As Coordinates) As PlacesResponse
            Return Me.Download(coordinates.Longitude, coordinates.Latitude, 1)
        End Function
        Public Function Download(ByVal coordinates As Coordinates, ByVal accuracy As Integer) As PlacesResponse
            Return Me.Download(coordinates.Longitude, coordinates.Latitude, accuracy)
        End Function
        Public Function Download(ByVal longitude As Double, ByVal latitude As Double, ByVal accuracy As Integer) As PlacesResponse
            Return Me.XmlToGeoFlickrPlacesResponse(MyBase.DownloadStream(Me.FlickrUrl(longitude, latitude, accuracy)))
        End Function
        Public Function DownloadParent(ByVal woeid As Long) As PlacesResponse
            Return Me.DownloadPlaces(Me.PlacesUrl("place/", woeid.ToString & "/parent", mOptions))
        End Function
        Public Function DownloadChildren(ByVal woeid As Long) As PlacesResponse
            Return Me.DownloadPlaces(Me.PlacesUrl("place/", woeid.ToString & "/children", mOptions))
        End Function
        Public Function DownloadChildren(ByVal woeid As Long, ByVal type As PlaceType) As PlacesResponse
            Return Me.DownloadPlaces(Me.PlacesUrl("place/", woeid.ToString & "/children.type(" & Convert.ToInt32(type).ToString & ")", mOptions))
        End Function
        Public Function DownloadChildren(ByVal woeid As Long, ByVal degree As Integer) As PlacesResponse
            Return Me.DownloadPlaces(Me.PlacesUrl("place/", woeid.ToString & "/children.degree(" & degree.ToString & ")", mOptions))
        End Function
        Public Function DownloadAncestors(ByVal woeid As Long) As PlacesResponse
            Return Me.DownloadPlaces(Me.PlacesUrl("place/", woeid.ToString & "/ancestors", mOptions))
        End Function
        Public Function DownloadCommonAncestor(ByVal first As Integer, ByVal woeids As IEnumerable(Of Long)) As PlacesResponse
            Return Me.DownloadPlaces(Me.PlacesUrl("place/", Me.ComAncestorQuery(first, woeids), mOptions))
        End Function
        Public Function DownloadBelongtos(ByVal woeid As Long) As PlacesResponse
            Return Me.DownloadPlaces(Me.PlacesUrl("place/", woeid.ToString & "/belongtos", mOptions))
        End Function
        Public Function DownloadBelongtos(ByVal woeid As Long, ByVal type As PlaceType) As PlacesResponse
            Return Me.DownloadPlaces(Me.PlacesUrl("place/", woeid.ToString & "/belongtos.type(" & Convert.ToInt32(type).ToString & ")", mOptions))
        End Function
        Public Function DownloadDescendants(ByVal woeid As Long) As PlacesResponse
            Return Me.DownloadPlaces(Me.PlacesUrl("place/", woeid.ToString & "/descendants", mOptions))
        End Function
        Public Function DownloadDescendants(ByVal woeid As Long, ByVal type As PlaceType) As PlacesResponse
            Return Me.DownloadPlaces(Me.PlacesUrl("place/", woeid.ToString & "/descendants.type(" & Convert.ToInt32(type).ToString & ")", mOptions))
        End Function
        Public Function DownloadNeighbors(ByVal woeid As Long) As PlacesResponse
            Return Me.DownloadPlaces(Me.PlacesUrl("place/", woeid.ToString & "/neighbors", mOptions))
        End Function
        Public Function DownloadNeighbors(ByVal woeid As Long, ByVal degree As Integer) As PlacesResponse
            Return Me.DownloadPlaces(Me.PlacesUrl("place/", woeid.ToString & "/descendants.degree(" & degree.ToString & ")", mOptions))
        End Function
        Public Function DownloadSiblings(ByVal woeid As Long) As PlacesResponse
            Return Me.DownloadPlaces(Me.PlacesUrl("place/", woeid.ToString & "/siblings", mOptions))
        End Function
        Public Function DownloadContinents() As PlacesResponse
            Return Me.DownloadPlaces(Me.PlacesUrl("continents", "", mOptions))
        End Function
        Public Function DownloadOceans() As PlacesResponse
            Return Me.DownloadPlaces(Me.PlacesUrl("oceans", "", mOptions))
        End Function
        Public Function DownloadSeas() As PlacesResponse
            Return Me.DownloadPlaces(Me.PlacesUrl("seas", "", mOptions))
        End Function
        Public Function DownloadCountries() As PlacesResponse
            Return Me.DownloadPlaces(Me.PlacesUrl("countries", "", mOptions))
        End Function
        Public Function DownloadStates(ByVal country As GeoPlaceData) As PlacesResponse
            Return Me.DownloadPlaces(Me.PlacesUrl("states/", country.WOEID.ToString, mOptions))
        End Function
        Public Function DownloadCounty(ByVal state As GeoPlaceData) As PlacesResponse
            Return Me.DownloadPlaces(Me.PlacesUrl("counties/", state.WOEID.ToString, mOptions))
        End Function
        Public Function DownloadDistricts(ByVal county As GeoPlaceData) As PlacesResponse
            Return Me.DownloadPlaces(Me.PlacesUrl("districts/", county.WOEID.ToString, mOptions))
        End Function

        Public Sub DownloadAsync(ByVal keyword As String, Optional ByVal userArgs As Object = Nothing)
            Me.DownloadAsync(keyword, PlaceType.Undefined, userArgs)
        End Sub
        Public Sub DownloadAsync(ByVal keyword As String, ByVal type As PlaceType, Optional ByVal userArgs As Object = Nothing)
            MyBase.DownloadStreamAsync(Me.PlacesUrl("places", Me.KeywordQuery(keyword, type), mOptions), userArgs)
        End Sub
        Public Sub DownloadAsync(ByVal woeids As IEnumerable(Of Long), Optional ByVal userArgs As Object = Nothing)
            MyBase.DownloadStreamAsync(Me.PlacesUrl("places", Me.WoeidQuery(woeids), mOptions), userArgs)
        End Sub
        Public Sub DownloadAsync(ByVal coordinates As Coordinates, Optional ByVal userArgs As Object = Nothing)
            Me.DownloadAsync(coordinates.Longitude, coordinates.Latitude, 1)
        End Sub
        Public Sub DownloadAsync(ByVal coordinates As Coordinates, ByVal accuracy As Integer, Optional ByVal userArgs As Object = Nothing)
            Me.DownloadAsync(coordinates.Longitude, coordinates.Latitude, accuracy)
        End Sub
        Public Sub DownloadAsync(ByVal longitude As Double, ByVal latitude As Double, ByVal accuracy As Integer, Optional ByVal userArgs As Object = Nothing)
            MyBase.DownloadStreamAsync(Me.FlickrUrl(longitude, latitude, accuracy), New IsDefaultUrlDlArgs(userArgs, False))
        End Sub
        Public Sub DownloadParentAsync(ByVal woeid As Long, Optional ByVal userArgs As Object = Nothing)
            MyBase.DownloadStreamAsync(Me.PlacesUrl("place/", woeid.ToString & "/parent", mOptions), userArgs)
        End Sub
        Public Sub DownloadChildrenAsync(ByVal woeid As Long, Optional ByVal userArgs As Object = Nothing)
            MyBase.DownloadStreamAsync(Me.PlacesUrl("place/", woeid.ToString & "/children", mOptions), userArgs)
        End Sub
        Public Sub DownloadChildrenAsync(ByVal woeid As Long, ByVal type As PlaceType, Optional ByVal userArgs As Object = Nothing)
            MyBase.DownloadStreamAsync(Me.PlacesUrl("place/", woeid.ToString & "/children.type(" & Convert.ToInt32(type).ToString & ")", mOptions), userArgs)
        End Sub
        Public Sub DownloadChildrenAsync(ByVal woeid As Long, ByVal degree As Integer, Optional ByVal userArgs As Object = Nothing)
            MyBase.DownloadStreamAsync(Me.PlacesUrl("place/", woeid.ToString & "/children.degree(" & degree.ToString & ")", mOptions), userArgs)
        End Sub
        Public Sub DownloadAncestorsAsync(ByVal woeid As Long, Optional ByVal userArgs As Object = Nothing)
            MyBase.DownloadStreamAsync(Me.PlacesUrl("place/", woeid.ToString & "/ancestors", mOptions), userArgs)
        End Sub
        Public Sub DownloadCommonAncestorAsync(ByVal first As Integer, ByVal woeids As IEnumerable(Of Long), Optional ByVal userArgs As Object = Nothing)
            MyBase.DownloadStreamAsync(Me.PlacesUrl("place/", Me.ComAncestorQuery(first, woeids), mOptions), userArgs)
        End Sub
        Public Sub DownloadBelongtosAsync(ByVal woeid As Long, Optional ByVal userArgs As Object = Nothing)
            Me.DownloadStreamAsync(Me.PlacesUrl("place/", woeid.ToString & "/belongtos", mOptions), userArgs)
        End Sub
        Public Sub DownloadBelongtosAsync(ByVal woeid As Long, ByVal type As PlaceType, Optional ByVal userArgs As Object = Nothing)
            Me.DownloadStreamAsync(Me.PlacesUrl("place/", woeid.ToString & "/belongtos.type(" & Convert.ToInt32(type).ToString & ")", mOptions), userArgs)
        End Sub
        Public Sub DownloadDescendantsAsync(ByVal woeid As Long, Optional ByVal userArgs As Object = Nothing)
            MyBase.DownloadStreamAsync(Me.PlacesUrl("place/", woeid.ToString & "/descendants", mOptions), userArgs)
        End Sub
        Public Sub DownloadDescendantsAsync(ByVal woeid As Long, ByVal type As PlaceType, Optional ByVal userArgs As Object = Nothing)
            MyBase.DownloadStreamAsync(Me.PlacesUrl("place/", woeid.ToString & "/descendants.type(" & Convert.ToInt32(type).ToString & ")", mOptions), userArgs)
        End Sub
        Public Sub DownloadNeighborsAsync(ByVal woeid As Long, Optional ByVal userArgs As Object = Nothing)
            MyBase.DownloadStreamAsync(Me.PlacesUrl("place/", woeid.ToString & "/neighbors", mOptions), userArgs)
        End Sub
        Public Sub DownloadNeighborsAsync(ByVal woeid As Long, ByVal degree As Integer, Optional ByVal userArgs As Object = Nothing)
            MyBase.DownloadStreamAsync(Me.PlacesUrl("place/", woeid.ToString & "/descendants.degree(" & degree.ToString & ")", mOptions), userArgs)
        End Sub
        Public Sub DownloadSiblingsAsync(ByVal woeid As Long, Optional ByVal userArgs As Object = Nothing)
            MyBase.DownloadStreamAsync(Me.PlacesUrl("place/", woeid.ToString & "/siblings", mOptions), userArgs)
        End Sub
        Public Sub DownloadContinentsAsync(Optional ByVal userArgs As Object = Nothing)
            MyBase.DownloadStreamAsync(Me.PlacesUrl("continents", "", mOptions), userArgs)
        End Sub
        Public Sub DownloadOceansAsync(Optional ByVal userArgs As Object = Nothing)
            MyBase.DownloadStreamAsync(Me.PlacesUrl("oceans", "", mOptions), userArgs)
        End Sub
        Public Sub DownloadSeasAsync(Optional ByVal userArgs As Object = Nothing)
            MyBase.DownloadStreamAsync(Me.PlacesUrl("seas", "", mOptions), userArgs)
        End Sub
        Public Sub DownloadCountriesAsync(Optional ByVal userArgs As Object = Nothing)
            MyBase.DownloadStreamAsync(Me.PlacesUrl("countries", "", mOptions), userArgs)
        End Sub
        Public Sub DownloadStatesAsync(ByVal country As GeoPlaceData, Optional ByVal userArgs As Object = Nothing)
            MyBase.DownloadStreamAsync(Me.PlacesUrl("states/", country.WOEID.ToString, mOptions), userArgs)
        End Sub
        Public Sub DownloadCountyAsync(ByVal state As GeoPlaceData, Optional ByVal userArgs As Object = Nothing)
            MyBase.DownloadStreamAsync(Me.PlacesUrl("counties/", state.WOEID.ToString, mOptions), userArgs)
        End Sub
        Public Sub DownloadDistrictsAsync(ByVal county As GeoPlaceData, Optional ByVal userArgs As Object = Nothing)
            MyBase.DownloadStreamAsync(Me.PlacesUrl("districts/", county.WOEID.ToString, mOptions), userArgs)
        End Sub



        Private Function DownloadPlaces(ByVal url As String) As PlacesResponse
            Return Me.XmlToGeoPlacesResponse(MyBase.DownloadStream(url))
        End Function
        Private Sub DownloadAsync_Completed(ByVal sender As Base.Download, ByVal e As Base.StreamDownloadCompletedEventArgs) Handles MyBase.AsyncStreamDownloadCompleted
            Dim args As PlacesDownloadCompletedEventArgs = Nothing
            If e.UserArgs IsNot Nothing AndAlso TypeOf e.UserArgs Is IsDefaultUrlDlArgs Then
                Dim dla = CType(e.UserArgs, IsDefaultUrlDlArgs)
                If dla.IsDefault Then
                    args = New PlacesDownloadCompletedEventArgs(dla.UserArgs, Me.XmlToGeoPlacesResponse(e.Response))
                Else
                    args = New PlacesDownloadCompletedEventArgs(dla.UserArgs, Me.XmlToGeoFlickrPlacesResponse(e.Response))
                End If
            Else
                args = New PlacesDownloadCompletedEventArgs(e.UserArgs, Me.XmlToGeoPlacesResponse(e.Response))
            End If
            RaiseEvent AsyncDownloadCompleted(Me, args)
        End Sub



        Private Function KeywordQuery(ByVal keyword As String, ByVal type As PlaceType) As String
            Dim query As String = String.Empty
            Dim q As String = String.Format(".q({0})", Uri.EscapeDataString(keyword))
            If type <> PlaceType.Undefined Then
                Dim t As String = String.Format(".type({0})", Convert.ToInt32(type).ToString)
                query = String.Format("$and({0},{1})", q, t)
            Else
                query = q
            End If
            Return query
        End Function
        Private Function WoeidQuery(ByVal woeids As IEnumerable(Of Long)) As String
            Dim query As New Text.StringBuilder(".woeid(")
            Dim lst As New List(Of Long)
            If woeids IsNot Nothing Then lst.AddRange(woeids)
            If lst.Count = 1 Then
                query.Append(lst(0).ToString)
            ElseIf lst.Count > 1 Then
                For i As Integer = 0 To lst.Count - 1
                    query.Append(lst(i).ToString)
                    If i <> lst.Count - 1 Then query.Append(","c)
                Next
            End If
            query.Append(")")
            Return query.ToString
        End Function
        Private Function ComAncestorQuery(ByVal first As Integer, ByVal woeids As IEnumerable(Of Long)) As String
            Dim lst As New List(Of Long)
            If woeids IsNot Nothing Then lst.AddRange(woeids)
            Dim query As New Text.StringBuilder()
            query.Append(first.ToString)
            query.Append("/common/")
            If lst.Count = 1 Then
                query.Append(lst(0).ToString)
            ElseIf lst.Count > 1 Then
                For i As Integer = 1 To lst.Count - 1
                    query.Append(lst(i).ToString)
                    If i <> lst.Count - 1 Then query.Append("/")
                Next
            End If
            Return query.ToString
        End Function

        Private Function FlickrUrl(ByVal longitude As Double, ByVal latitude As Double, ByVal accuracy As Integer) As String
            Return mHelper.YqlUrl("*", "flickr.places", "lat=" & latitude.ToString & " and lon=" & longitude & " and accuracy=" & accuracy.ToString, mOptions, False)
        End Function
        Private Function PlacesUrl(ByVal collection As String, ByVal query As String, ByVal opt As PlacesDownloadOptions) As String
            If opt.ApplicationID Is Nothing OrElse opt.ApplicationID.Trim = String.Empty Then Throw New ArgumentNullException("appID", "The Yahoo! Application ID is empty. You need to pass a valid Application ID to use Yahoo! Web Services. For more information go to: http://developer.yahoo.com/faq/#appid")
            Dim startCount As String = String.Empty
            Dim cult As String = String.Empty
            If opt.Culture IsNot Nothing And collection.StartsWith("place") Then
                cult = String.Format("&lang={0}-{1}", opt.Culture.Language.ToString, opt.Culture.Country.ToString)
            End If
            If opt IsNot Nothing AndAlso (opt.Start >= 0 And opt.Count > 0) Then startCount = ";start=" & opt.Start.ToString & ";count=" & opt.Count.ToString
            Return String.Format("http://where.yahooapis.com/v1/{0}{1}{2}?appid={3}&format=xml&view=long{4}", collection, query, startCount, opt.ApplicationID, cult)
        End Function

        Private Function XmlToGeoPlacesResponse(ByVal resp As Base.StreamResponse) As PlacesResponse
            Dim places As New List(Of GeoPlaceData)
            Dim results() As XmlNode = mHelper.GetResultsFromXml(resp.Result, "place")
            For Each node As XmlNode In results
                Dim p As GeoPlaceData = Me.ToPlace(node)
                If p IsNot Nothing Then places.Add(p)
            Next
            Return New PlacesResponse(resp.Connection, places.ToArray)
        End Function
        Private Function XmlToGeoFlickrPlacesResponse(ByVal resp As Base.StreamResponse) As PlacesResponse
            Dim places As New List(Of GeoPlaceData)
            Dim results() As XmlNode = mHelper.GetResultsFromXml(resp.Result, "place")
            For Each node As XmlNode In results
                Dim p As New GeoPlaceData

                Dim lat As String = mHelper.GetXmlAttributeValue(node, "latitude")
                Dim lon As String = mHelper.GetXmlAttributeValue(node, "longitude")
                Dim name As String = mHelper.GetXmlAttributeValue(node, "name")
                Dim id As String = mHelper.GetXmlAttributeValue(node, "place_type_id")
                Dim woeid As String = mHelper.GetXmlAttributeValue(node, "woeid")

                Double.TryParse(lat, p.Center.Latitude)
                Double.TryParse(lon, p.Center.Longitude)
                p.Name = name
                Dim t As Integer = 0
                If Integer.TryParse(id, t) Then p.Type = DirectCast(t, PlaceType)
                Long.TryParse(woeid, p.WOEID)

                places.Add(p)
            Next
            Return New PlacesResponse(resp.Connection, places.ToArray)
        End Function



        Private Function ToPlace(ByVal node As XmlNode) As GeoPlanet.GeoPlaceData
            If node IsNot Nothing AndAlso node.Name.ToLower = "place" Then
                Dim p As New GeoPlanet.GeoPlaceData
                For Each prpNode As XmlNode In node.ChildNodes
                    Select Case prpNode.Name.ToLower
                        Case "woeid"
                            Long.TryParse(prpNode.InnerText, p.WOEID)
                        Case "placetypename"
                            Dim code As String = mHelper.GetXmlAttributeValue(prpNode, "code")
                            If code <> String.Empty Then p.Type = DirectCast(Convert.ToInt32(code), PlaceType)
                        Case "name"
                            p.Name = prpNode.InnerText
                        Case "country"
                            Dim ctr As New GeoPlanet.AdminArea
                            ctr.Code = mHelper.GetXmlAttributeValue(prpNode, "code")
                            ctr.Name = prpNode.InnerText
                        Case "admin1"
                            If prpNode.InnerText <> String.Empty Then
                                Dim admin1 As New GeoPlanet.FederalAdminArea
                                admin1.Code = mHelper.GetXmlAttributeValue(prpNode, "code")
                                admin1.Name = prpNode.InnerText
                                Dim type As String = mHelper.GetXmlAttributeValue(prpNode, "type").ToLower
                                If type <> String.Empty Then
                                    For i As GeoPlanet.FederalAdminAreaType = 0 To GeoPlanet.FederalAdminAreaType.State
                                        If i.ToString.ToLower = type Then
                                            admin1.AdminType = i
                                            Exit For
                                        End If
                                    Next
                                End If
                                p.FederalAdmin = admin1
                            End If
                        Case "admin2"
                            If prpNode.InnerText <> String.Empty Then
                                Dim admin2 As New GeoPlanet.RegionalAdminArea
                                admin2.Code = mHelper.GetXmlAttributeValue(prpNode, "code")
                                admin2.Name = prpNode.InnerText
                                Dim type As String = mHelper.GetXmlAttributeValue(prpNode, "type").ToLower
                                If type <> String.Empty Then
                                    For i As GeoPlanet.RegionalAdminAreaType = 0 To GeoPlanet.RegionalAdminAreaType.Province
                                        If i.ToString.ToLower = type Then
                                            admin2.AdminType = i
                                            Exit For
                                        End If
                                    Next
                                End If
                                p.RegionalAdmin = admin2
                            End If
                        Case "admin3"
                            If prpNode.InnerText <> String.Empty Then
                                p.LocalAdmin = New GeoPlanet.LocalAdminArea
                                p.LocalAdmin.Code = mHelper.GetXmlAttributeValue(prpNode, "code")
                                p.LocalAdmin.Name = prpNode.InnerText
                                Dim type As String = mHelper.GetXmlAttributeValue(prpNode, "type").ToLower
                                If type <> String.Empty Then
                                    For i As GeoPlanet.LocalAdminAreaType = 0 To GeoPlanet.LocalAdminAreaType.Ward
                                        If i.ToString.ToLower = type Then
                                            p.LocalAdmin.AdminType = i
                                            Exit For
                                        End If
                                    Next
                                End If
                            End If
                        Case "locality1"
                            If prpNode.InnerText <> String.Empty Then
                                p.Locality1 = New GeoPlanet.Locality
                                p.Locality1.Type = mHelper.GetXmlAttributeValue(prpNode, "type").ToLower
                                p.Locality1.Name = prpNode.InnerText
                            End If
                        Case "locality2"
                            If prpNode.InnerText <> String.Empty Then
                                p.Locality2 = New GeoPlanet.Locality
                                p.Locality2.Type = mHelper.GetXmlAttributeValue(prpNode, "type").ToLower
                                p.Locality2.Name = prpNode.InnerText
                            End If
                        Case "postal"
                            If prpNode.InnerText <> String.Empty Then p.PostalCode = prpNode.InnerText
                        Case "centroid"
                            p.Center = GetCoordinates(prpNode)
                        Case "boundingbox"
                            Dim b As New CoordinatesRectangle
                            For Each directionNode As XmlNode In prpNode.ChildNodes
                                Select Case directionNode.Name.ToLower
                                    Case "southwest" : b.SouthWest = GetCoordinates(directionNode)
                                    Case "northeast" : b.NorthEast = GetCoordinates(directionNode)
                                End Select
                            Next
                            p.BoundingBox = b
                        Case "arearank"
                            Dim i As Integer = 0
                            Integer.TryParse(prpNode.InnerText, i)
                            p.AreaInSquareKilometers = GetAreaRank(i)
                        Case "poprank"
                            Dim i As Integer = 0
                            Integer.TryParse(prpNode.InnerText, i)
                            p.PopulationCount = GetPopRank(i)
                    End Select
                Next
                Return p
            Else
                Return Nothing
            End If
        End Function


        Private Function GetCoordinates(ByVal node As XmlNode) As Coordinates
            Dim lat, lng As Double
            For Each coordNode As XmlNode In node.ChildNodes
                Select Case coordNode.Name.ToLower
                    Case "latitude"
                        Double.TryParse(coordNode.InnerText, Globalization.NumberStyles.Any, New Globalization.CultureInfo("en-US"), lat)
                    Case "longitude"
                        Double.TryParse(coordNode.InnerText, Globalization.NumberStyles.Any, New Globalization.CultureInfo("en-US"), lng)
                End Select
            Next
            Return New Coordinates(lng, lat)
        End Function
        Private Function GetPopRank(ByVal type As Integer) As GeoPlanet.ValueRange
            Select Case type
                Case 19 : Return New GeoPlanet.ValueRange(type, 1000000000, 3000000000)
                Case 20 : Return New GeoPlanet.ValueRange(type, 3000000000, 10000000000)
                Case Else : Return GetAreaRank(type)
            End Select
        End Function
        Private Function GetAreaRank(ByVal type As Integer) As GeoPlanet.ValueRange
            Select Case type
                Case 1 : Return New GeoPlanet.ValueRange(type, 1, 3)
                Case 2 : Return New GeoPlanet.ValueRange(type, 3, 10)
                Case 3 : Return New GeoPlanet.ValueRange(type, 10, 30)
                Case 4 : Return New GeoPlanet.ValueRange(type, 30, 100)
                Case 5 : Return New GeoPlanet.ValueRange(type, 100, 300)
                Case 6 : Return New GeoPlanet.ValueRange(type, 300, 1000)
                Case 7 : Return New GeoPlanet.ValueRange(type, 1000, 3000)
                Case 8 : Return New GeoPlanet.ValueRange(type, 3000, 10000)
                Case 9 : Return New GeoPlanet.ValueRange(type, 10000, 30000)
                Case 10 : Return New GeoPlanet.ValueRange(type, 30000, 100000)
                Case 11 : Return New GeoPlanet.ValueRange(type, 100000, 300000)
                Case 12 : Return New GeoPlanet.ValueRange(type, 300000, 1000000)
                Case 13 : Return New GeoPlanet.ValueRange(type, 1000000, 3000000)
                Case 14 : Return New GeoPlanet.ValueRange(type, 3000000, 10000000)
                Case 15 : Return New GeoPlanet.ValueRange(type, 10000000, 30000000)
                Case 16 : Return New GeoPlanet.ValueRange(type, 30000000, 100000000)
                Case 17 : Return New GeoPlanet.ValueRange(type, 100000000, 300000000)
                Case 18 : Return New GeoPlanet.ValueRange(type, 300000000, 1000000000)
                Case Else : Return New GeoPlanet.ValueRange()
            End Select
        End Function



    End Class


    Friend Class IsDefaultUrlDlArgs
        Inherits Base.DownloadEventArgs
        Public Property IsDefault As Boolean
        Public Sub New(ByVal args As Object, ByVal isdef As Boolean)
            MyBase.New(args)
            Me.IsDefault = isdef
        End Sub
    End Class




    Public Class PlacesDownloadOptions
        Inherits ResultCountOptions
        Public Property ApplicationID As String
        Public Property Culture As Culture

        Public Sub New()
        End Sub
        Public Sub New(ByVal resultsCount As ResultCountOptions)
            MyBase.New(resultsCount)
        End Sub
        Public Sub New(ByVal resultsCount As ResultCountOptions, ByVal appID As String)
            MyBase.New(resultsCount)
            Me.ApplicationID = appID
        End Sub
        Friend Sub New(ByVal original As PlacesDownloadOptions)
            MyBase.New(original)
            Me.ApplicationID = original.ApplicationID
        End Sub
    End Class



    Public Class PlacesDownloadCompletedEventArgs
        Inherits Base.DownloadCompletedEventArgs
        Private mKeywords() As String
        Private mWoeids() As Integer
        Private mPlaceType As PlaceType = PlaceType.Undefined
        Private mDegree As Integer
        Public ReadOnly Property Keywords() As String()
            Get
                Return mKeywords
            End Get
        End Property
        Public ReadOnly Property WOEIDs() As Integer()
            Get
                Return mWoeids
            End Get
        End Property
        Public ReadOnly Property PlaceType() As PlaceType
            Get
                Return mPlaceType
            End Get
        End Property
        Public ReadOnly Property Degree() As Integer
            Get
                Return mDegree
            End Get
        End Property
        Public Overloads ReadOnly Property Response() As PlacesResponse
            Get
                Return DirectCast(MyBase.Response, PlacesResponse)
            End Get
        End Property
        Friend Sub New(ByVal userArgs As Object, ByVal resp As PlacesResponse)
            MyBase.New(userArgs, resp)
        End Sub
    End Class



    Public Class PlacesResponse
        Inherits Base.Response
        Public Overloads ReadOnly Property Result() As GeoPlaceData()
            Get
                Return TryCast(MyBase.Result, GeoPlaceData())
            End Get
        End Property
        Friend Sub New(ByVal info As Base.ConnectionInfo, ByVal result As GeoPlaceData())
            MyBase.New(info, result)
        End Sub
    End Class





End Namespace